﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using LitJson;

[AddComponentMenu("PUEX/Affector/Physics/PlaneCollider")]
public class PUEXPlaneColliderAffector : PUEXBaseColliderAffector {

	#region - Editable property
	[Header("PlaneCollider:")]
	public Vector3 center;
	public string centerParamName = "";
	public Vector3 normal;
	public string normalParamName = "";
	#endregion

	#region - Property
	protected Plane _colliderPlane;
	#endregion
	
	#region - Lifecycle
	public PUEXPlaneColliderAffector ()
	{
		center = Vector3.zero;
		normal = Vector3.up;
	}
	#endregion

	#region - Update
	public override void preUpdate (float dt)
	{
		base.preUpdate (dt);

		Vector3 center_ = center;
		Vector3 normal_ = normal;
		if (inWorldSpace) {
			if (ownerEmitter.keepLocal) {
				Matrix4x4 invMat = ownerEmitter.ownerParticleSystem.systemToWorldTransform.inverse;
				center_ = invMat.MultiplyPoint(center_);
				normal_ = invMat.MultiplyVector(normal_);
			}
		} else {
			if (!ownerEmitter.keepLocal) {
				center_ = ownerEmitter.ownerParticleSystem.systemToWorldTransform.MultiplyPoint(center_);
				normal_ = ownerEmitter.ownerParticleSystem.systemToWorldTransform.MultiplyVector(normal_);
			}
		}

		_colliderPlane = new Plane (normal_, center_);
	}
	
	public override void update (float dt, PUEXParticle particle)
	{
		Vector3 predictedPosition = particle.position + particle.velocity() * dt;
		bool collision = false;
		
		switch (intersectionType) {
		case IntersectionType.IT_SPHERE:
		{
			Sphere particleColliderSphere = new Sphere (predictedPosition, particle.boundingSphereRadius());

			if (PUEXUtils.intersects (particleColliderSphere, _colliderPlane))
				collision = true;
			
			break;
		}
		case IntersectionType.IT_BOX:
		{
			Bounds particleColliderBox = new Bounds (predictedPosition, particle.dimensions ());

			if (PUEXUtils.intersects (_colliderPlane, particleColliderBox))
				collision = true;
			
			break;
		}
		}
		
		if (collision)
		{
			particle.addEventFlags(PUEXParticle.PEF_COLLIDED);
			
			if (minKillSpeed > 0f && Mathf.Abs(particle.speed) <= minKillSpeed) {
				particle.kill();
			}

			if (minFreezeSpeed > 0f && !particle.freezed && Mathf.Abs(particle.speed) <= minFreezeSpeed) {
				particle.freeze ();
			}
			
			if (particle.timeToLive > 0f && !particle.freezed) {
				switch (collisionType) {
				case CollisionType.CT_BOUNCE:
				{
					/** If the particle is on the plane or at the back of the plane, bounce it.
		                Make use of the same formula as the sphere collider.
		            */
					particle.direction = 2f * (Vector3.Dot(-particle.direction, _colliderPlane.normal)) * _colliderPlane.normal + particle.direction;
					particle.speed *= bouncyness;
					
					break;
				}
				case CollisionType.CT_FLOW:
				{
					/** change the direction parallel to the plane.
		            */
					particle.direction = PUEXUtils.planeProjectVector(_colliderPlane, particle.direction).normalized;
					particle.speed *= friction;
					
					break;
				}
				}
				
				calculateRotationSpeedAfterCollision(particle);
			}
		}
	}
	#endregion

	#region - Param property
	public override void updateParams (PUEXParticleSystem system)
	{
		Vector3 value;
		if (system.getParamVec3 (centerParamName, out value)) {
			center = value;
		}
		if (system.getParamVec3 (normalParamName, out value)) {
			normal = value;
		}
	}
	#endregion

	#region - Serialization
	public override JsonData save (JsonData emitterData, JsonData systemData)
	{
		JsonData ret = base.save (emitterData, systemData);

		ret ["center"] = PUEXUtils.serializeVector3(center);
		ret ["center_param_name"] = centerParamName;
		ret ["normal"] = PUEXUtils.serializeVector3(normal);
		ret ["normal_param_name"] = normalParamName;
		
		return ret;
	}
	#endregion

}
